/**
 * 本模块是类似axios的uni请求实例封装。支持请求 下载 上传
 * 同步使用方式	this.muajax.apiname([params])  同步执行，直接赋值
 * 异步使用方式	this.muajax.apiname([params]).then(result=>{}).catch()  在then回调中接受结果
 * 快捷方法		this.muajax(url,[config],[callback],[config])	默认为post方式，config与axios()参数相同  有callback时为异步方法，无callback时调用流程同上
 * 				this.muajax.get(url,[data],[callback],[config])	固定请求方式为get,其他使用注意同上
 * 				this.muajax.post(url,[data],[callback],[config])	固定请求方式为post,其他使用注意同上
 * 所有的api组放在commonapi中，如需拆分api文件在commonapi依次引入即可，如需动态载入子模块api方法使用muajax.initapi(apiConfigList)进行手动加载 如果功能较多推荐模块动态加载方式
 * @Author   Tao 1755885716@qq.com
 * 典型应用示例
 * async ()=>{
 * 		let rs = await muajax.apiname({id:2});
 * 		console.log(rs);
 * }
 * 接口配置信息示例 muajax.initapi([config])
 * @params otherparam quest FN_PARAMS
 * @Params method 请求方式
 * @Params url 请求地址
 * @Params desc 请求说明
 * @Params mock mock地址
 * @params requestType :request默认 uploadFile 文件上传 downloadFile文件下载
 * @Params dontRrefreshToken 不需要token刷新
 * @Params dontUseToken 接口不需要传递token进行账户登录校验
 * @Params passSuccessful 直接放行响应成功 default:false 直接放行的接口不走拦截器
 * @Params passLose 直接放行响应失败 default:false 直接放行的接口不走拦截器
 * 加密功能
 * @Params encipher:false,//启用加密 （传输数据加密 不含一维字段名 二维以上对象加密过程自动转一维）
 * @Params encipherKey:false,//字段名加密
 * @Params decrypt:false,//启用解密
 * @Params decryptKey:false,//字段名需要解密
 * 防抖（仅时间范围内最后一个执行）和节流（仅时间范围内第一个执行） 可用于防重复提交功能  可同时使用但是不建议  节流防抖时间不计算在api的timeout内
 * @Params debounceTime:0  default-0 默认防抖时间为0，即为不需要，配置单位为ms,此处配置建议值：300，根据实际情况调整  建议使用在数据搜索请求，动态下拉动态检索等
 * @Params debounceKey 自定义防抖标识  默认为接口url 可以自定义为任意名称，也可多接口使用同一名称实现优先进入队列获取优先请求权限，也可将url+参数序列化之后的字符实现接口不重复参数请求
 * @Params throttleTime:0  default-0 默认节流时间为0，即为不需要，配置单位为ms,配置建议值：800，根据实际情况调整  建议使用在数据提交请求畅姐
 * @Params throttleKey 自定义节流标识  作用和使用方式同上
 * confirm和loading
 * @Params confirm 请求toast.confirmg配置  默认为false  配置示例：{...confirm配置}
 * @Params loading 请求toast.loading配置  默认为false  配置示例：{status:Ref(loadingBool),inst:Ref(loadingInst),...loading配置}
 * @Params urlRewrite(url)方法  config扩展了urlRewrite(url)方法 ，用于在请求发起前对url进行重写，可自定义重写规则，如：url重写后缀，url重写参数，url重写参数值等
 * 示例muajax.apiFunName(data,{urlRewrite:url=>url+'/123123'})
 * config扩展 toast.loading和toast.confirm支持
 * muajax.apiFunName(data,{
 *  confirm:{
 * 		...confirm配置
 * 	},
 * 	loading:{
 * 		status:Ref(loadingBool),//loading状态
 * 		inst: Ref(loadingInst),//loading实例  仅未销毁前有效  小程序不支持
 * 		...loading配置
 *  },
 * })
 */

import apilis from './api-common.js';
import store from '../store'
import Toast from './Toast.js';
import * as crypto from './crypto'
import setting from './setting.js';
import ThorttleAndDebounce from './muajax-thorttleAndDebounce.js'
import {
	encryptBase64,
	encryptWithAes,
	generateAesKey,
	decryptWithAes,
	decryptBase64
} from './crypto/crypto';
import {
	encrypt,
	decrypt
} from './crypto/jsencrypt';

let ReauestFilter = new ThorttleAndDebounce(); //防抖和节流实现类初始化
/**
 * loading和confirm
 * @param {*} api
 * @param {*} config
 * @returns
 */
async function useLoadingConfirm(config) {
	let confirm = config.confirm ? config.confirm : false
	if (confirm && !await Toast.confirm(confirm)) return false;
	let loading = config.loading ? config.loading : false
	if (loading) {
		if (!loading.status) loading.status = {
			value: true
		}
		else loading.status.value = true
		Toast.loading(loading)
	}
	return {
		loading,
		confirm
	}
}
/**
 * 注销loading
 * @param {*} loading loading配置
 */
function destoryLoading(loading) {
	if (loading?.status?.value) {
		loading.status.value = false
		uni.hideLoading()
	}
}

// 当前版本不支持env文件
const VUE_APP_BASE_URL_LOCAL = 'https://t3.web.xn--xhq521b/prod-api'
const VUE_APP_BASE_URL = 'https://t3.web.xn--xhq521b/prod-api'
const VUE_APP_BASE_URL_DEV = 'https://t3.web.xn--xhq521b/prod-api'
const VUE_APP_BASE_URL_TEST = 'https://t3.web.xn--xhq521b/prod-api'
// const VUE_APP_BASE_URL_LOCAL = 'http://xmldc.w1.luyouxia.net'
// const VUE_APP_BASE_URL = 'http://xmldc.w1.luyouxia.net'
// const VUE_APP_BASE_URL_DEV = 'http://xmldc.w1.luyouxia.net'
// const VUE_APP_BASE_URL_TEST = 'http://xmldc.w1.luyouxia.net'

/**
 * 处理请求前参数
 * @param {Object} api 接口配置
 * @param {Object} config 额外动态配置
 * @return {Object} config 配置合并后的结果
 */
function requestBefore(api, {
	data,
	formData
}, config) {
	// 环境基础url解析
	let baseUrl = VUE_APP_BASE_URL
	// let ver = uni.getAccountInfoSync()
	// if (ver?.miniProgram?.envVersion == 'trial') baseUrl = VUE_APP_BASE_URL_TEST || baseUrl
	// else if (ver?.miniProgram?.envVersion == 'develop') baseUrl = VUE_APP_BASE_URL_LOCAL || baseUrl
	const devBaseUrl = uni.getStorageSync('dev_BASE_URL') || ''
	if (devBaseUrl) baseUrl = devBaseUrl
	// console.log(baseUrl)
	// 配置信息处理 url
	let url = config.url || api.url || config.moclurl || api.moclurl
	url = (url.substr(0, 7) == 'http://' || url.substr(0, 8) == 'https://') ? url : baseUrl + url;
	url = config.urlRewrite ? config.urlRewrite(url) : url
	if (config.urlRewrite) delete config.urlRewrite
	// 配置信息处理 配置聚合
	config = {
		sslVerify: false,
		...api,
		dontRrefreshToken: api.dontRrefreshToken || false,
		dontUseToken: api.dontUseToken || false,
		...config,
		url,
	}
	if (api.method) config.method = api.method.toUpperCase()
	config.header = config.header || {}
	// 设置请求token
	if (store.state.token && !config.dontUseToken) {
		config.header[setting.tokenField] = 'Bearer ' + store.state.token
	}

	// 设置请求头标识参数
	let isEncrypt = config.isEncrypt;
	config.header['Content-Type'] = 'application/json;charset=utf-8';
	config.header['clientid'] = '428a8310cd442757ae699df5d894f051';
	config.header['istoken'] = 'true';
	if (config.isEncrypt) {
		config.header['isEncrypt'] = config.isEncrypt;
	}

	// 数据合并和加密
	// let encipher = config.encipher === undefined ? setting.encipher : config.encipher
	// let encipherKey = config.encipherKey === undefined ? setting.encipherKey : config.encipherKey
	if (data) {
		config.data = {
			...(api.data || {}),
			...(data || {}),
			...(config.data || {}),
		}
		// if (encipher) {
		// config.data = crypto.encryption(config.data, encipherKey)
		// }
		if (isEncrypt && (config.method === 'post' || config.method === 'POST' || config.method === 'PUT' || config
				.method === 'put')) {
			console.log(config.data)
			// 生成一个 AES 密钥
			const aesKey = generateAesKey();
			config.header['encrypt-key'] = encrypt(encryptBase64(aesKey));
			config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) :
				encryptWithAes(config.data, aesKey);
		}
	}
	if (formData) {
		config.formData = {
			...(api.formData || {}),
			...(formData || {}),
			...(config.formData || {}),
		}
		// if (encipher) {
		// 	config.formData = crypto.encryption(config.formData, encipherKey)
		// }
		if (isEncrypt && (api.method.method === 'post' || config.method === 'POST' || config.method === 'PUT' || api
				.method.method === 'put')) {
			console.log(config.data)
			// 生成一个 AES 密钥
			const aesKey = generateAesKey();
			config.header['encrypt-key'] = encrypt(encryptBase64(aesKey));
			config.formData = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.formData),
				aesKey) : encryptWithAes(config.formData, aesKey);
		}
	}
	return config
}
/**
 * 请求完成
 * @param {Object} res 请求结果
 * @param {Object} config 是否直接放行请求返回
 * @param {*} callback 回调
 */
function requestAfterSuccess(res, config, callback) {
	// 解码处理
	// let decrypt = config.decrypt === undefined ? setting.decrypt : config.decrypt
	// let decryptKey = config.decryptKey === undefined ? setting.decryptKey : config.decryptKey
	// if (decrypt) {
	// 	res.data = crypto.decryption(res.data, decryptKey)
	// }
	// if (res.data && typeof res.data == 'string') {
	// 	try {
	// 		res.data = JSON.parse(res.data)
	// 	} catch (e) {}
	// }
	//  加密后的 AES 秘钥
	// const keyStr = config.headers['encrypt-key'];
	// // 加密
	// if (keyStr != null && keyStr != '') {
	//   const data = res.data;
	//   // 请求体 AES 解密
	//   const base64Str = decrypt(keyStr);
	//   // base64 解码 得到请求头的 AES 秘钥
	//   const aesKey = decryptBase64(base64Str.toString());
	//   // aesKey 解码 data
	//   const decryptData = decryptWithAes(data, aesKey);
	//   // 将结果 (得到的是 JSON 字符串) 转为 JSON
	//   res.data = JSON.parse(decryptData);
	// }
	// 二进制数据则直接返回
	// if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
	//   return res.data;
	// } 
	// 解码处理
	let decrypt = config.decrypt === undefined ? setting.decrypt : config.decrypt
	let decryptKey = config.decryptKey === undefined ? setting.decryptKey : config.decryptKey
	if (decrypt) {
		res.data = crypto.decryption(res.data, decryptKey)
	}
	if (res.data && typeof res.data == 'string') {
		try {
			res.data = JSON.parse(res.data)
		} catch (e) {}
	}
	// http状态处理
	let passSuccessful = config.passSuccessful || false
	if (passSuccessful) {
		callback(res.data)
	} else {
		let code =  res.data.code 
		decodeURI()
		if (code == 200) {
			callback(res.data)
		} else {
			console.log(res)
			Toast.err(res.data.message || res.data.msg || res.msg || res.err || res.error);
			if (code == 401) {
				uni.reLaunch({
					url: '/pages/login/login'
				})
			} else if (code == 500) {
				// uni.reLaunch({
				// 	url: '/pages/login/login'
				// })
			}
		}

	}
}
/**
 * 请求失败
 * @param {Object} res 请求结果
 * @param {boolean} [passLose=false] 是否直接放行错误返回
 * @param {*} callback 回调
 */
function requestAfterFail(err, {
	passLose = false
}, callback) {
	callback(passLose ? err : {
		code: 0,
		msg: err.errMsg
	})
}
/**
 * 请求结束
 * @param {Object} res 请求结果
 * @param {Object} data 传参
 * @param {Object} config 请求配置
 */
function requestComplete(res, data, config) {
	destoryLoading(config.loading)
	// if(config.file || config.filePath) console.log('request:',config.url,"\nformData:",config.formData,"\nfilePath:",config.file || config.filePath,"\nreturn:",res,"\nconfig:",config)
	// else console.log('request:',config.url,"\nsend:",config.data,"\nreturn:",res,"\nconfig:",config)
	if (setting.requestAfterRfToken && !config.dontRrefreshToken) refreshToken()
}
/**
 * 获取token
 */
async function refreshToken() {
	// if(!store.checkRefresh()) return
	let rs = await muajax.refreshToken()
	if (rs.code == 200) {
		// 存储token和用户信息
		store.saveData('token', rs.data, true)
		// store.saveData('user',rs.data,true)
		if (rs.data.tokenExpires) setting.tokenExpires = rs.data.tokenExpires
	} else {
		// 刷新失败 直接清除登陆信息跳转登陆
		setting.tokenRfErrToLogin && store.loginOut()
	}
}
/**
 * 计算刷新间隔 自动运行token刷新
 */
async function autoRefreshToken() {
	let tokenRefreshTime = 0
	if (setting.tokenRefreshTime > 1) tokenRefreshTime = setting.tokenRefreshTime
	else if (setting.tokenRefreshTime > 0) tokenRefreshTime = setting.tokenRefreshTime * setting.tokenExpires
	if (tokenRefreshTime > 0) {
		await refreshToken()
		setTimeout(() => autoRefreshToken(), tokenRefreshTime)
	}
}
// 应用启动5秒后自启动
(function() {
	setting.autoRefreshToken && setTimeout(() => autoRefreshToken(), 5000)
})()

/**
 * 请求方法
 * @param {*} api 接口配置信息  至少需要包含url和method
 * @param {*} data 接口数据
 * @param {*} config 接口发起式的私有配置
 * @param {*} callback 接口请求回调
 * @returns  存在请求回调则执行回调返回 否则使用返回promise结果
 */
function request(api, data = {}, config = {}, callback) {
	return new Promise(async function(resolve, reject) {
		config = requestBefore(api, {
			data
		}, {
			dataType: 'json',
			timeout: 20000,
			...config
		})
		if (!await ReauestFilter.repetition(config)) return (callback || resolve)({}) //防抖和节流
		if (!await useLoadingConfirm(config)) return (callback || resolve)({}) //询问和加载效果
		let requestTask = uni.request({
			...config,
			success: (res) => requestAfterSuccess(res, config, callback || resolve),
			fail: (err) => requestAfterFail(err, config, callback || resolve),
			complete: (res) => requestComplete(res, config.data || {}, config)
		});
		if (config.onProgressUpdate) {
			config.onProgressUpdate(requestTask)
			delete config.onProgressUpdate
		}
	})
}
/**
 * 文件上传
 * @param {} api 接口配置
 * @param {*} [formData={}]
 * @param {{}} [config={}]
 * @param {*} callback
 */
function uploadFile(api, formData = {}, config = {}, callback) {
	return new Promise(async function(resolve, reject) {
		config = requestBefore(api, {
			formData
		}, {
			timeout: 120000,
			// header:{
			// 'Content-Type':'multipart/form-data'
			// },
			// name: 'files',
			...config,
		})
		let lcf = await useLoadingConfirm(config) //询问和加载效果
		if (!lcf) {
			if (callback) return callback({})
			return resolve({});
		}
		let requestTask = uni.uploadFile({
			...config,
			success: (res) => requestAfterSuccess(res, config, callback || resolve),
			fail: (err) => requestAfterFail(err, config, callback || resolve),
			complete: (res) => requestComplete(res, config.formData || {}, config)
		})
		if (config.onProgressUpdate) {
			let onProgressUpdate = config.onProgressUpdate
			requestTask.onProgressUpdate(res => onProgressUpdate(requestTask, res))
			delete config.onProgressUpdate
		}
	})
}
/**
 * 请求分发
 * @param {*} api 接口配置信息
 * @param {{}} [formData={}] 数据
 * @param {{}} [config={}] 动态配置信息
 * @param {*} callback 回调
 */
function downloadFile(api, formData = {}, config = {}, callback) {
	return new Promise(async function(resolve, reject) {
		config = requestBefore(api, {
			formData
		}, {
			timeout: 120000,
			...config,
		})
		let lcf = await useLoadingConfirm(config) //询问和加载效果
		if (!lcf) {
			if (callback) return callback({})
			return resolve({});
		}
		let requestTask = uni.downloadFile({
			...config,
			success: (res) => requestAfterSuccess(res, config, callback || resolve),
			fail: (err) => requestAfterFail(err, config, callback || resolve),
			complete: (res) => requestComplete(res, config.formData || {}, config)
		})
		if (config.onProgressUpdate) {
			let onProgressUpdate = config.onProgressUpdate
			requestTask.onProgressUpdate(res => onProgressUpdate(requestTask, res))
			delete config.onProgressUpdate
		}
	})
}
/**
 * 请求分发
 * @param {*} api 接口配置信息
 * @param {{}} [data={}] 数据
 * @param {{}} [config={}] 动态配置信息
 * @param {*} callback 回调
 */
function send(api, data = {}, config = {}, callback) {
	let requestType = api.requestType || config.requestType || ''
	switch (requestType) {
		case 'uploadFile':
			return uploadFile(api, data, config, callback);
		case 'downloadFile':
			return downloadFile(api, data, config, callback);
		default:
			return request(api, data, config, callback)
	}
}

/**
 * 请求
 */
function muajax(url, config = {}, callback) {
	return send({
		url,
		method: 'post',
		time: new Date().getTime()
	}, {}, config, callback);
}
muajax.get = function(url, data = {}, callback, config = {}) {
	return send({
		url,
		method: 'get',
		time: new Date().getTime()
	}, data, config, callback);
}
muajax.post = function(url, data = {}, callback, config = {}) {
	return send({
		url,
		method: 'post',
		time: new Date().getTime()
	}, data, config, callback);
}

/**
 * 加载api和预处理方法
 * @param {*} useobj 方法挂载对象
 * @param {*} apilis  所有为系统配置好的默认请求
 * @param {*} conf  系统请求默认conf
 */
function apiListReady(useobj, apilis, conf = {}) {
	for (let k in apilis) {
		let api = apilis[k];
		if (!!useobj[k]) {
			console.warn('接口被重复定义', api)
			continue
		}

		useobj[k] = async (data = {}, config = {}, callback) => {
			return send(api, data, {
				...conf,
				...config
			}, callback)
		}
	}
}
// 载入系统默认api挂载到主方法
apiListReady(muajax, apilis)

/**
 * 动态载入其他模块私有api
 * @param {*} mylist
 * @param {*} conf 创建示例时设置该实例的基础config
 * @return {object}
 */
muajax.initapi = function(mylist, conf = {}) {
	let myapi = {
		mylist
	};
	apiListReady(myapi, mylist, conf)
	myapi.get = muajax.get;
	myapi.post = muajax.post;
	return myapi;
}



export default muajax